package com.dhcc.sds.busi.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.dhcc.cdc.busi.entity.CdcEpisodeEntity;
import com.dhcc.cdc.sys.dao.DictionaryMapper;
import com.dhcc.core.framework.base.service.impl.BaseServiceImpl;
import com.dhcc.core.framework.util.CommonUtil;
import com.dhcc.core.framework.util.RedisUtil;
import com.dhcc.sds.busi.dao.AnalysisSdsCusIndstaMapper;
import com.dhcc.sds.busi.dto.*;
import com.dhcc.sds.busi.service.*;
import com.dhcc.sds.custom.entity.SdsCusIndexFieldEntity;
import com.dhcc.sds.custom.entity.SdsCusIndexHeaderEntity;
import com.dhcc.sds.custom.service.ISdsCusIndexFieldService;
import com.dhcc.sds.custom.service.ISdsCusIndexHeaderService;
import com.dhcc.sds.standard.dao.SdsStandSourceMapper;
import com.dhcc.sds.standard.entity.SdsStandSourceEntity;
import com.dhcc.sds.standard.function.GetValueSrv;
import com.dhcc.sds.util.DateUtil;
import com.dhcc.sds.util.StrUtil;
import org.apache.commons.lang.StringEscapeUtils;
import org.pentaho.di.core.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 80572
 * @version 1.0
 * @date 2021/8/8 17:36
 */
@Service
public class AnalysisSdsCusIndstaServiceImpl extends BaseServiceImpl<AnalysisSdsCusIndstaMapper,AnalysisSdsCusIndstaDto> implements IAnalysisSdsCusIndstaService {

    @Autowired
    private ISdsCusIndexFieldService sdsCusIndexFieldService;
    @Autowired
    private ISdsCusIndexHeaderService sdsCusIndexHeaderService;
    @Autowired
    private SdsStandSourceMapper sdsStandSourceMapper;
    @Autowired
    private DictionaryMapper dictionaryMapper;
    @Autowired
    private GetValueSrv getValueSrv;

    @Autowired
    private RedisUtil redisUtil;
    // 取值用redis缓存key关键词
    private static String redisKeyWords = "GetValue_";
    /**
     * 小括号及其里面内容
     */
    private static String p1 = "\\(.*?\\)";
    /**
     * 中括号及其里面内容
     */
    private static String p2 = "\\[.*?\\]";
    /**
     * 大括号及其里面内容
     */
    private static String p3 = "\\{.*?\\}";


    @Override
    public List<AnalysisSdsCusIndstaDto> selectIndexTableData(Map<String, Object> map) {
        List<Map<String, String>> resMapList = new ArrayList<>();
        List<CdcEpisodeEntity> cdcEpisodeEntities = baseMapper.selectInCaseEpisode(map);
        List<AnalysisSdsCusIndstaDto> resultList = baseMapper.selectIndexTableData(map);
        if (cdcEpisodeEntities.size()==0) return resultList;
        a:for (AnalysisSdsCusIndstaDto analysisSdsCusIndstaDto : resultList) {
            String express = analysisSdsCusIndstaDto.getExpress();
            if (ObjectUtils.isEmpty(express)) continue;
            Wrapper<SdsCusIndexFieldEntity> fieldWrp = new EntityWrapper<>();
            fieldWrp.eq("index_header_id",analysisSdsCusIndstaDto.getSecId());
            List<SdsCusIndexFieldEntity> indexFieldEntities = sdsCusIndexFieldService.selectList(fieldWrp);
            for (SdsCusIndexFieldEntity indexFieldEntity : indexFieldEntities) {
                Long datasource = indexFieldEntity.getDatasource();
                SdsStandSourceEntity source = sdsStandSourceMapper.selectById(datasource);
                if (!ObjectUtils.isEmpty(source)) {
                    SdsStandSourceEntity parentSourceEntity = sdsStandSourceMapper.selectById(source.getParentId());
                    String methodName = parentSourceEntity.getSourceCode();
                    if ("CustomEntity".equals(methodName)){
                        Map<String,String> returnMap = getValueSrv.CustomEntity(cdcEpisodeEntities, datasource);
                        analysisSdsCusIndstaDto.setResult(returnMap.get("value"));
                        analysisSdsCusIndstaDto.setNumerator(returnMap.get("numerator"));
                        analysisSdsCusIndstaDto.setDenominator(returnMap.get("denominator"));
                        continue a;
                    }else {
                        try {
                            Class GetValueSrv = Class.forName("com.dhcc.sds.standard.function.GetValueSrv");
                            GetValueSrv function = (GetValueSrv) GetValueSrv.newInstance();
                            Method method = GetValueSrv.getMethod(methodName, Long.class, Long.class, Integer.class, Long.class, Long.class);
                            for (CdcEpisodeEntity cdcEpisodeEntity : cdcEpisodeEntities) {
                                List<List<Map<String, Object>>> resList = (List<List<Map<String, Object>>>) method.invoke(function, cdcEpisodeEntity.getId(),datasource, null, indexFieldEntity.getDic(), indexFieldEntity.getValueTime());
                                Map<String, String> map1 = calcItemVal(cdcEpisodeEntity.getId(),indexFieldEntity, resList);
                                resMapList.add(map1);
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
            Map<String,Object> returnMap = exp(express,cdcEpisodeEntities,resMapList,indexFieldEntities);
            resMapList.clear();
            analysisSdsCusIndstaDto.setResult(Objects.toString(returnMap.get("value")));
            analysisSdsCusIndstaDto.setNumerator(Objects.toString(returnMap.get("numerator")));
            analysisSdsCusIndstaDto.setDenominator(Objects.toString(returnMap.get("denominator")));
        }
        return resultList;

    }

    @Override
    public JSONObject selectDetails(Map<String, Object> params) {
        JSONObject caseObj = new JSONObject();
        SdsCusIndexHeaderEntity cusIndexHeaderEntity = sdsCusIndexHeaderService.selectById(Objects.toString(params.get("headerId")));
        if (CommonUtil.isEmpty(cusIndexHeaderEntity)) return caseObj;
        params.put("entityId",cusIndexHeaderEntity.getEntityId());
        params.put("express",cusIndexHeaderEntity.getExpress());

        String numerator = Objects.toString(params.get("numerator"));
        String denominator = Objects.toString(params.get("denominator"));
        String title = cusIndexHeaderEntity.getTitle()+"：";
        if (!CommonUtil.equals("0",denominator)){
            BigDecimal numerator1 = new BigDecimal(numerator);
            BigDecimal denominator1 = new BigDecimal(denominator);
            BigDecimal r = numerator1.divide(denominator1,2,RoundingMode.HALF_UP);
            //将结果转化成百分比
            NumberFormat percent = NumberFormat.getPercentInstance();
            percent.setMaximumFractionDigits(2);
            String value = percent.format(r.doubleValue());
            title += value+" | "+numerator+"/"+denominator;
        } else {
            title += numerator;
        }
        caseObj.put("title",title);
        caseObj.put("locChartData", this.selectChart(params,"Loc"));
        caseObj.put("DocChartData", this.selectChart(params,"Doc"));

        return caseObj;
    }

    public List<AnalysisSdsCusChartDto> selectChart(Map<String, Object> map, String type)  {
        List<Map<String, String>> resMapList = new ArrayList<>();
        List<AnalysisSdsCusChartDto> analysisSdsCusChartDtos = new ArrayList<>();
        String express = Objects.toString(map.get("express"));
        express = StringEscapeUtils.unescapeHtml(express);
        Wrapper<SdsCusIndexFieldEntity> fieldWrp = new EntityWrapper<>();
        fieldWrp.eq("index_header_id",Objects.toString(map.get("headerId")));
        List<SdsCusIndexFieldEntity> indexFieldEntities = sdsCusIndexFieldService.selectList(fieldWrp);
        Set<Long> Ids =new HashSet<>();
        List<CdcEpisodeEntity> cdcEpisodeEntities = baseMapper.selectInCaseEpisode(map);
        if (CommonUtil.equals("Loc",type)){
            for (CdcEpisodeEntity cdcEpisodeEntity : cdcEpisodeEntities) {
                Ids.add(cdcEpisodeEntity.getAdmLocId());
            }
        }else if(CommonUtil.equals("Doc",type)) {
            for (CdcEpisodeEntity cdcEpisodeEntity : cdcEpisodeEntities) {
                Ids.add(cdcEpisodeEntity.getAdmDoctorId());
            }
        }

        a:for (Long id : Ids) {
            List<CdcEpisodeEntity> cdcTypeEpisodeEntities = new ArrayList<>();
            for (CdcEpisodeEntity cdcEpisodeEntity : cdcEpisodeEntities) {
                if (CommonUtil.equals("Loc",type)&&CommonUtil.equals(id,cdcEpisodeEntity.getAdmLocId())){
                    cdcTypeEpisodeEntities.add(cdcEpisodeEntity);
                }
                if (CommonUtil.equals("Doc",type)&&CommonUtil.equals(id,cdcEpisodeEntity.getAdmDoctorId())){
                    cdcTypeEpisodeEntities.add(cdcEpisodeEntity);
                }
            }
            AnalysisSdsCusChartDto analysisSdsCusChartDto = new AnalysisSdsCusChartDto();
            analysisSdsCusChartDto.setLocDesc(cdcTypeEpisodeEntities.get(0).getAdmLocDesc());
            analysisSdsCusChartDto.setDocName(cdcTypeEpisodeEntities.get(0).getAdmDoctorName());
            analysisSdsCusChartDto.setLocId(cdcTypeEpisodeEntities.get(0).getAdmLocId());
            analysisSdsCusChartDto.setDocId(cdcTypeEpisodeEntities.get(0).getAdmDoctorId());
            analysisSdsCusChartDto.setEntityId(Objects.toString(map.get("entityId")));
            analysisSdsCusChartDto.setStartDate(Objects.toString(map.get("startDate")));
            analysisSdsCusChartDto.setEndDate(Objects.toString(map.get("endDate")));
            for (SdsCusIndexFieldEntity indexFieldEntity : indexFieldEntities) {
                Long datasource = indexFieldEntity.getDatasource();
                SdsStandSourceEntity source = sdsStandSourceMapper.selectById(datasource);
                if (!ObjectUtils.isEmpty(source)) {
                    SdsStandSourceEntity parentSourceEntity = sdsStandSourceMapper.selectById(source.getParentId());
                    String methodName = parentSourceEntity.getSourceCode();
                    if ("CustomEntity".equals(methodName)){
                        Map<String,String> returnMap = getValueSrv.CustomEntity(cdcTypeEpisodeEntities, datasource);
                        analysisSdsCusChartDto.setValue(returnMap.get("value"));
                        analysisSdsCusChartDto.setEpIds(Objects.toString(returnMap.get("epIds")));
                        analysisSdsCusChartDto.setEpIdsOpp(Objects.toString(returnMap.get("epIdsOpp")));
                        analysisSdsCusChartDtos.add(analysisSdsCusChartDto);
                        continue a;
                    }else {
                        try {
                            Class GetValueSrv = Class.forName("com.dhcc.sds.standard.function.GetValueSrv");
                            GetValueSrv function = (GetValueSrv) GetValueSrv.newInstance();
                            Method method = GetValueSrv.getMethod(methodName, Long.class, Long.class, Integer.class, Long.class, Long.class);
                            for (CdcEpisodeEntity cdcLocEpisodeEntity : cdcTypeEpisodeEntities) {
                                List<List<Map<String, Object>>> resList = (List<List<Map<String, Object>>>) method.invoke(function, cdcLocEpisodeEntity.getId(),datasource, null, indexFieldEntity.getDic(), indexFieldEntity.getValueTime());
                                Map<String, String> map1 = calcItemVal(cdcLocEpisodeEntity.getId(),indexFieldEntity, resList);
                                resMapList.add(map1);
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
            Map<String,Object> returnMap = exp(express,cdcTypeEpisodeEntities,resMapList,indexFieldEntities);
            resMapList.clear();
            analysisSdsCusChartDto.setEpIds(Objects.toString(returnMap.get("epIds")));
            analysisSdsCusChartDto.setEpIdsOpp(Objects.toString(returnMap.get("epIdsOpp")));
            analysisSdsCusChartDto.setValue(Objects.toString(returnMap.get("value")));
            analysisSdsCusChartDtos.add(analysisSdsCusChartDto);
        }
        return analysisSdsCusChartDtos;
    }

    /**
     * 计算结果值
     */
    private Map<String, String> calcItemVal(Long episodeId, SdsCusIndexFieldEntity indexFieldEntity, List<List<Map<String, Object>>> result) {
        Map<String, String> resultMap = new HashMap<>();
        a:for (List<Map<String, Object>> list : result) {
            for (Map<String, Object> map : list) {
                SdsStandSourceEntity sdsStandSourceEntity = sdsStandSourceMapper.selectById(indexFieldEntity.getDatasource());
                String fieldCode = sdsStandSourceEntity.getSourceCode();
                if (CommonUtil.isEmpty(map.get(fieldCode))) {
                    continue;
                }
                String resultStr = StrUtil.clearSpecialChar(map.get(fieldCode).toString());
                if (!StringUtil.isEmpty(resultStr)) {
                    resultMap.put(episodeId+"_"+indexFieldEntity.getCode(),resultStr);
                    break a;
                }
            }
        }
        return resultMap;
    }


    private Map<String,Object> exp(String express,List<CdcEpisodeEntity> cdcEpisodeEntities,List<Map<String, String>> resMapList,List<SdsCusIndexFieldEntity> indexFieldEntities){
        //分子
        Integer numerator = 0;
        //分母
        Integer denominator = 0;
        StringBuilder epIds = new StringBuilder("-1,");
        StringBuilder epIdsOpp = new StringBuilder("-1,");
        Map<String,Object> returnMap = new HashMap<>();
        for (CdcEpisodeEntity cdcEpisodeEntity : cdcEpisodeEntities) {
            Map<String,Object> tem = new HashMap<>();
            for (SdsCusIndexFieldEntity indexFieldEntity : indexFieldEntities) {
                String code = indexFieldEntity.getCode();
                for (Map<String, String> resMap : resMapList) {
                    String res = resMap.get(cdcEpisodeEntity.getId()+"_"+code);
                    if (StringUtils.isEmpty(res)) continue;
                    tem.put(code,res);
                    break;
                }
            }

            Integer integer = calcTriggerExp(express, tem);
            if (integer==1){
                numerator += 1;
                epIds.append(cdcEpisodeEntity.getId()).append(",");
            }else {
                epIdsOpp.append(cdcEpisodeEntity.getId()).append(",");
            }
            denominator += 1;

        }
        returnMap.put("epIds",epIds.toString());
        returnMap.put("epIdsOpp",epIdsOpp.toString());
        returnMap.put("numerator",numerator);
        returnMap.put("denominator",denominator);
        if (denominator == 0) denominator = 1;
        BigDecimal numerator1 = new BigDecimal(numerator);
        BigDecimal denominator1 = new BigDecimal(denominator);
        BigDecimal r = numerator1.divide(denominator1,2,RoundingMode.HALF_UP);
        //将结果转化成百分比
        NumberFormat percent = NumberFormat.getPercentInstance();
        percent.setMaximumFractionDigits(2);
        String value = percent.format(r.doubleValue());
        returnMap.put("value",value);
        return returnMap;
    }

    public Integer calcTriggerExp(String express,Map<String, Object> editMap) {
        // 项目全部默认显示
        Integer editPower = 1;
        //1、ScriptEngineManager是JDK6提出的相关方法，这方式的主要目的就是用来对脚本语言的处理。
        //2、ScriptEngineManager是在java的javax.script的包下，是Java原生提供的脚本语言处理方法。
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        if (CommonUtil.isEmpty(express)) {
            return editPower;
        } else {
            // 带表达式的项目默认不显示，只有表达式条件成立之后才显示
            editPower = 0;
            String triggerExp = express;
            // 查找规则公式中括号以内的字符
            Pattern p = Pattern.compile(p1);// 查找规则公式中括号以内的字符
            // 替换关联项目的值
            Matcher m = p.matcher(triggerExp);
            while (m.find()) {// 遍历找到的所有括号
                if (m.group().contains("\"")) { // 如果里面是常量，则继续下一个
                    continue;
                }
                // 取得关联项目代码
                String itemCode = m.group().substring(1, m.group().length() - 1);

                String linkVal;
                if (ObjectUtils.isEmpty(editMap.get(itemCode))){
                    linkVal = "(\"\")";
                }else {
                    linkVal = "(\"" + editMap.get(itemCode) + "\")";
                }
                // 如果表达式有引号，那么是字符串比较，给项目值加上双引号
                //linkVal = "(\"" + linkVal + "\")";
                // 将值替换表达式中项目代码
                triggerExp = triggerExp.replace(m.group(), linkVal);
            }
            if (!triggerExp.matches(p3)) {
                String[] expArr = triggerExp.split(":", 2);
                String subExpType_l = expArr[0];
                String subExp_l = expArr[1];

                long divisor = 1;
                if (subExpType_l.contains("CALC")) {
                    switch (subExpType_l) {
                        case "CALC-h":
                            // 1小时毫秒数
                            divisor = 60 * 60 * 1000;
                            break;
                        case "CALC-m":
                            // 一分钟毫秒数
                            divisor = 60 * 1000;
                            break;
                        case "CALC-day":
                            // 一天毫秒数
                            divisor = 24 * 60 * 60 * 1000;
                            break;
                        case "CALC-month":
                            // 一个月毫秒数
                            divisor = 30l * 24l * 60l * 60l * 1000l;
                            break;
                        case "CALC-year":
                            // 一年毫秒数
                            divisor = 12l * 30l * 24l * 60l * 60l * 1000l;
                            break;
                        default:
                            break;
                    }
                    // 查找规则公式中小括号以内的值
                    // 替换关联项目的值
                    m = p.matcher(subExp_l);
                    String execExp = subExp_l;
                    while (m.find()) {
                        if ("()".equals(m.group()) || "(\"\")".equals(m.group())) {
                            execExp = execExp.replace(m.group(), "0");
                            continue;
                        }
                        String val = m.group().substring(1, m.group().length() - 1).replace("\"", "");
                        Long times = DateUtil.getTime(val);
                        int minutes = Math.toIntExact(times / divisor);
                        execExp = execExp.replace(val, String.valueOf(minutes));
                    }
                    triggerExp = triggerExp.replace(subExp_l, execExp);
                }
                triggerExp = triggerExp.replace(subExpType_l, "");
            } else {
                // 查找规则公式中大括号以内的字符
                Pattern pat3 = Pattern.compile(p3);
                // 替换关联项目的值
                Matcher m3 = pat3.matcher(triggerExp);
                while (m3.find()) {
                    // 大括号中间的表达式
                    String subExpStr_l = m3.group().substring(1, m3.group().length() - 1);
                    String[] expArr = subExpStr_l.split(":", 2);
                    String subExpType_l = expArr[0];
                    String subExp_l = expArr[1];

                    long divisor = 1;
                    if (subExpType_l.contains("CALC")) {
                        switch (subExpType_l) {
                            case "CALC-h":
                                // 1小时毫秒数
                                divisor = 60 * 60 * 1000;
                                break;
                            case "CALC-m":
                                // 一分钟毫秒数
                                divisor = 60 * 1000;
                                break;
                            case "CALC-day":
                                // 一天毫秒数
                                divisor = 24 * 60 * 60 * 1000;
                                break;
                            case "CALC-month":
                                // 一个月毫秒数
                                divisor = 30l * 24l * 60l * 60l * 1000l;
                                break;
                            case "CALC-year":
                                // 一年毫秒数
                                divisor = 12l * 30l * 24l * 60l * 60l * 1000l;
                                break;
                            default:
                                break;
                        }
                        // 查找规则公式中小括号以内的值
                        // 替换关联项目的值
                        m = p.matcher(subExp_l);
                        String execExp = subExp_l;
                        while (m.find()) {
                            if ("()".equals(m.group()) || "(\"\")".equals(m.group())) {
                                execExp = execExp.replace(m.group(), "0");
                                continue;
                            }
                            String val = m.group().substring(1, m.group().length() - 1).replace("\"", "");
                            Long times = DateUtil.getTime(val);
                            int minutes = Math.toIntExact(times / divisor);
                            execExp = execExp.replace(val, String.valueOf(minutes));
                        }
                        triggerExp = triggerExp.replace(subExp_l, execExp);
                    }
                    triggerExp = triggerExp.replace(subExpStr_l, subExp_l);
                    triggerExp = triggerExp.replace(subExpType_l, "");
                }
            }
            triggerExp = triggerExp.replace(":", "");
            if (triggerExp.contains("contains")) {
                triggerExp = modifyExpression(triggerExp);
            }
            // 将表达式中的大括号中括号替换为小括号，以及其他其他字符替换为可执行字符
            triggerExp = triggerExp.replace("{", "(").replace("}", ")");
            triggerExp = triggerExp.replace("[", "(").replace("]", ")");
            triggerExp = triggerExp.replace("contains(\"\")", "contains(\",,\")");
            triggerExp = triggerExp.replace("oth", "OTH").replace("def", "DEF");

            // 运算前置条件表达式
            try {
                if ((boolean) engine.eval(triggerExp)) {
                    editPower = 1;
                }
            } catch (ScriptException e) {
                // 表达式解析失败，默认返回1
                return 1;
            }
        }
        return editPower;
    }



    public String modifyExpression(String expression) {
        String aExp = expression;

        //判断表达式中有无中括号
        if (!expression.matches(p2)) {
            if (!expression.contains("contains")) {
                return aExp;
            }
            // 循环处理原始表达式中的子表达式
            String[] subStrArr = aExp.split(".contains");
            for (int ExpInd = 0; ExpInd < subStrArr.length; ExpInd++) {
                String expSub = subStrArr[ExpInd];  // 提取子表达式
                // 提取引号内的内容
                String regExp = "(\").*?(\")";
                Pattern pattern = Pattern.compile(regExp);
                Matcher matcher = pattern.matcher(expSub);
                while (matcher.find()) {
                    String group = matcher.group();
                    String result = group.substring(1, group.length() - 1);
                    result = "\"," + result + ",\"";
                    aExp = aExp.replaceFirst(group, result);
                }
            }
        } else {
            // 查找规则公式中中括号以内的字符
            Pattern pat2 = Pattern.compile(p2);
            // 替换关联项目的值
            Matcher m2 = pat2.matcher(aExp);
            while (m2.find()) {
                String subGroup = m2.group();
                if (!subGroup.contains("contains")) {
                    continue;
                }
                String tmpGroup = subGroup;
                String tmpSubFroup = subGroup.substring(1, subGroup.length() - 1);
                // 循环处理原始表达式中的子表达式
                String[] subStrArr = tmpSubFroup.split(".contains");
                for (int ExpInd = 0; ExpInd < subStrArr.length; ExpInd++) {
                    String expSub = subStrArr[ExpInd];  // 提取子表达式
                    // 提取引号内的内容
                    String regExp = "(\").*?(\")";
                    Pattern pattern = Pattern.compile(regExp);
                    Matcher matcher = pattern.matcher(expSub);
                    while (matcher.find()) {
                        String group = matcher.group();
                        String result = group.substring(1, group.length() - 1);
                        result = "\"," + result + ",\"";
                        tmpGroup = tmpGroup.replaceFirst(group, result);
                    }
                }
                aExp = aExp.replace(subGroup, tmpGroup);
            }
        }

        return aExp;
    }

}
